<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>PF: Packet Filtering</title>
<link rev="made" href="mailto:www@openbsd.org">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="resource-type" content="document">
<meta name="description"   content="the OpenBSD FAQ page">
<meta name="keywords"      content="openbsd,faq,pf">
<meta name="distribution"  content="global">
</head>

<!--
Copyright (c) 2003-2007 Joel Knight <enabled@myrealbox.com>

Permission to use, copy, modify, and distribute this documentation for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.

THE DOCUMENTATION IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS DOCUMENTATION INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS DOCUMENTATION
-->

<body bgcolor="#ffffff" text="#000000">
<!-- Passes validator.w3.org, please keep it this way;
please, use a max of 72 chars per line -->

<a href="../../index.html">
<img alt="[OpenBSD]" height=30 width=141 src="../../images/smalltitle.gif" border="0">
</a>
<p>
[<a href="tables.html">Previous: Tables</a>]
[<a href="index.html">Contents</a>]
[<a href="nat.html">Next: Network Address Translation</a>]

<h1><font color="#e00000">PF: Packet Filtering</font></h1>

<hr>

<h3>Table of Contents</h3>
<ul>
<li><a href="#intro">Introduction</a>
<li><a href="#syntax">Rule Syntax</a>
<li><a href="#defdeny">Default Deny</a>
<li><a href="#pass">Passing Traffic</a>
<li><a href="#quick">The <tt>quick</tt> Keyword</a>
<li><a href="#state">Keeping State</a>
<li><a href="#udpstate">Keeping State for UDP</a>
<li><a href="#stateopts">Stateful Tracking Options</a>
<li><a href="#tcpflags">TCP Flags</a>
<li><a href="#synproxy">TCP SYN Proxy</a>
<li><a href="#antispoof">Blocking Spoofed Packets</a>
<li><a href="#urpf">Unicast Reverse Path Forwarding</a>
<li><a href="#osfp">Passive Operating System Fingerprinting</a>
<li><a href="#ipopts">IP Options</a>
<li><a href="#example">Filtering Ruleset Example</a>
</ul>

<hr>

<a name="intro"></a>
<h2>Introduction</h2>
Packet filtering is the selective passing or blocking of data
packets as they pass through a network interface. The criteria that 
<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=pf&amp;sektion=4&amp;manpath=OpenBSD+4.3"
>pf(4)</a> uses when inspecting packets are based on the Layer 3 
(<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=ip&amp;sektion=4"
>IPv4</a> and 
<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=ip6&amp;sektion=4"
>IPv6</a>) and Layer 4
(<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=tcp&amp;sektion=4"
>TCP</a>,
<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=udp&amp;sektion=4"
>UDP</a>,
<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=icmp&amp;sektion=4"
>ICMP</a>, and
<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=icmp6&amp;sektion=4"
>ICMPv6</a>) headers. The most often used criteria are source and
destination address, source and destination port, and protocol.

<p>
Filter rules specify the criteria that a packet must match and the
resulting action, either block or pass, that is taken when a match is
found. Filter rules are evaluated in sequential order, first to last.
Unless the packet matches a rule containing the <tt>quick</tt> keyword,
the packet will be evaluated against <i>all</i> filter rules before the
final action is taken. The last rule to match is the "winner" and will
dictate what action to take on the packet. There is an implicit
<tt>pass all</tt> at the beginning of a filtering ruleset meaning that if a
packet does not match any filter rule the resulting action will be
<tt>pass</tt>.

<a name="syntax"></a>
<h2>Rule Syntax</h2>
The general, <i>highly simplified</i> syntax for filter rules is:
<blockquote>
<tt>
<i>action</i> [<i>direction</i>] [log] [quick] [on <i>interface</i>] 
[<i>af</i>] [proto <i>protocol</i>] \<br>
&nbsp;&nbsp;&nbsp;[from <i>src_addr</i> [port <i>src_port</i>]] [to 
<i>dst_addr</i> [port <i>dst_port</i>]] \<br>
&nbsp;&nbsp;&nbsp;[flags <i>tcp_flags</i>] [<i>state</i>]
</tt>
</blockquote>

<dl>
<dt><tt><i>action</i></tt>
<dd>The action to be taken for matching packets, either <tt>pass</tt> or
<tt>block</tt>. The <tt>pass</tt> action will pass the packet back to
the kernel for further processing while the <tt>block</tt> action will
react based on the setting of the
<a href="options.html#block-policy"><tt>block-policy</tt></a> option. 
The default reaction may be overridden by specifying either <tt>block
drop</tt> or <tt>block return</tt>.

<dt><tt><i>direction</i></tt>
<dd>The direction the packet is moving on an interface, either
<tt>in</tt> or <tt>out</tt>.

<dt><tt>log</tt>
<dd>Specifies that the packet should be logged via
<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=pflogd&amp;sektion=8&amp;manpath=OpenBSD+4.3"
>pflogd(8)</a>. If the rule creates state then only the
packet which establishes the state is logged. 
To log all packets regardless, use <tt>log (all)</tt>.

<dt><tt>quick</tt>
<dd>If a packet matches a rule specifying <tt>quick</tt>, then that rule
is considered the last matching rule and the specified 
<tt><i>action</i></tt> is taken.

<dt><tt><i>interface</i></tt>
<dd>The name or group of the network interface that the packet is moving
through.
Interfaces can be added to arbitrary groups using the
<a
href="http://www.openbsd.org/cgi-bin/man.cgi?query=ifconfig&amp;sektion=8"
>ifconfig(8)</a> command.
Several groups are also automatically created by the kernel:
<ul>
<li>The <tt>egress</tt> group, which contains the interface(s) that
holds the default route(s).
<li>Interface family group for cloned interfaces.
For example: <tt>ppp</tt> or <tt>carp</tt>.
</ul>
This would cause the rule to match for any packet traversing any
<tt>ppp</tt> or <tt>carp</tt> interface, respectively.

<dt><tt><i>af</i></tt>
<dd>The address family of the packet, either <tt>inet</tt> for IPv4 or
<tt>inet6</tt> for IPv6. PF is usually able to determine this parameter
based on the source and/or destination address(es).

<dt><tt><i>protocol</i></tt>
<dd>The Layer 4 protocol of the packet:
<ul>
<li><tt>tcp</tt>
<li><tt>udp</tt>
<li><tt>icmp</tt>
<li><tt>icmp6</tt>
<li>A valid protocol name from 
<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=protocols&amp;sektion=5"
><tt>/etc/protocols</tt></a>
<li>A protocol number between 0 and 255
<li>A set of protocols using a <a href="macros.html#lists">list</a>.
</ul>

<dt><tt><i>src_addr</i></tt>, <tt><i>dst_addr</i></tt> 
<dd>The source/destination address in the IP header. Addresses can be
specified as:
<ul>
<li>A single IPv4 or IPv6 address.
<li>A <a href="http://public.pacbell.net/dedicated/cidr.html">CIDR</a> 
network block.
<li>A fully qualified domain name that will be resolved via DNS when the
ruleset is loaded. All resulting IP addresses will be substituted into
the rule.
<li>The name of a network interface or group.
Any IP addresses assigned to the
interface will be substituted into the rule.
<li>The name of a network interface followed by
<tt>/<i>netmask</i></tt> (i.e., <tt>/24</tt>). Each IP address on the
interface is combined with the netmask to form a CIDR network block
which is substituted into the rule.
<li>The name of a network interface or group in parentheses <tt>( )</tt>.
This tells PF to update the rule if the IP address(es) on the named interface
change.
This is useful on an interface that gets its IP address via DHCP
or dial-up as the ruleset doesn't have to be reloaded each time the
address changes.
<li>The name of a network interface followed by any one of these
modifiers: 
  <ul>
  <li><tt>:network</tt> - substitutes the CIDR network block (e.g.,
  192.168.0.0/24)
  <li><tt>:broadcast</tt> - substitutes the network broadcast address
  (e.g., 192.168.0.255)
  <li><tt>:peer</tt> - substitutes the peer's IP address on a
  point-to-point link
  </ul>
  <dl>
  <dd>In addition, the <tt>:0</tt> modifier can be appended to either an
  interface name or to any of the above modifiers to indicate that PF
  should not include aliased IP addresses in the substitution. 
  These modifiers can also be used when the interface is contained in
  parentheses.
  Example: <tt>fxp0:network:0</tt>
  </dl>
<li>A <a href="tables.html">table</a>.
<li>The keyword <tt>urpf-failed</tt> can be used for the source address
to indicate that it should be run through the
<a href="#urpf">uRPF check</a>.
<li>Any of the above but negated using the <tt>!</tt> ("not") modifier.
<li>A set of addresses using a <a href="macros.html#lists">list</a>.
<li>The keyword <tt>any</tt> meaning all addresses
<li>The keyword <tt>all</tt> which is short for <tt>from any to
any</tt>.
</ul>

<dt><tt><i>src_port</i></tt>, <tt><i>dst_port</i></tt>
<dd>The source/destination port in the Layer 4 packet header. Ports can
be specified as:
<ul>
<li>A number between 1 and 65535
<li>A valid service name from
<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=services&amp;sektion=5"
><tt>/etc/services</tt></a>
<li>A set of ports using a <a href="macros.html#lists">list</a>
<li>A range:
	<ul>
	<li><tt>!=</tt> (not equal)
	<li><tt>&lt;</tt> (less than)
	<li><tt>&gt;</tt> (greater than)
	<li><tt>&lt;=</tt> (less than or equal)
	<li><tt>&gt;=</tt> (greater than or equal)
	<li><tt>&gt;&lt;</tt> (range)
	<li><tt>&lt;&gt;</tt> (inverse range)
	<dl>
	<dd>The last two are binary operators (they take two arguments) and
	do not include the arguments in the range.
	</dl>
	<li><tt>:</tt> (inclusive range)
	<dl>
	<dd>The inclusive range operator is also a binary operator and does
	include the arguments in the range.
	</dl>
	</ul>
</ul>

<dt><tt><i>tcp_flags</i></tt>
<dd>Specifies the flags that must be set in the TCP header when using
<tt>proto tcp</tt>. Flags are specified as 
<tt>flags <i>check</i>/<i>mask</i></tt>. For example: <tt>flags
S/SA</tt> - this instructs PF to only look at the S and A (SYN and ACK)
flags and to match if only the SYN flag is "on".
In OpenBSD 4.1 and later, the default flags <tt>S/SA</tt> are applied to all
TCP filter rules.

<dt><tt><i>state</i></tt>
<dd>Specifies whether state information is kept on packets matching this
rule.
<ul>
<li><tt>keep state</tt> - works with TCP, UDP, and ICMP.
In OpenBSD 4.1 and later, this option is the default for all filter rules.
<li><tt>modulate state</tt> - works only with TCP. PF will generate
strong Initial Sequence Numbers (ISNs) for packets matching this rule.
<li><tt>synproxy state</tt> - proxies incoming TCP connections to help
protect servers from spoofed TCP SYN floods. 
This option includes the functionality of <tt>keep state</tt> and
<tt>modulate state</tt>.
</ul>
</dl>

<a name="defdeny"></a>
<h2>Default Deny</h2>
The recommended practice when setting up a firewall is to take a
"default deny" approach. That is, to deny <i>everything</i> and then
selectively allow certain traffic through the firewall. This approach is
recommended because it errs on the side of caution and also makes
writing a ruleset easier.

<p>
To create a default deny filter policy, the first two filter rules should
be:
<blockquote>
<tt>
block in &nbsp;all<br>
block out all
</tt>
</blockquote>

<p>
This will block all traffic on all interfaces in either direction from
anywhere to anywhere.

<a name="pass"></a>
<h2>Passing Traffic</h2>
Traffic must now be explicitly passed through the firewall or it will be
dropped by the default deny policy. This is where packet criteria such
as source/destination port, source/destination address, and protocol
come into play. Whenever traffic is permitted to pass through the
firewall the rule(s) should be written to be as restrictive as 
possible. This is to ensure that the intended traffic, and only the
intended traffic, is permitted to pass.

<p>
Some examples:
<blockquote>
<tt>
# Pass traffic in on dc0 from the local network, 192.168.0.0/24,<br>
# to the OpenBSD machine's IP address 192.168.0.1. Also, pass the<br>
# return traffic out on dc0.<br>
pass in &nbsp;on dc0 from 192.168.0.0/24 to 192.168.0.1<br>
pass out on dc0 from 192.168.0.1 to 192.168.0.0/24<br>
<br>
<br>
# Pass TCP traffic in on fxp0 to the web server running on the<br>
# OpenBSD machine. The interface name, fxp0, is used as the<br>
# destination address so that packets will only match this rule if<br>
# they're destined for the OpenBSD machine.<br>
pass in on fxp0 proto tcp from any to fxp0 port www
</tt>
</blockquote>

<a name="quick"></a>
<h2>The <tt>quick</tt> Keyword</h2>
As indicated earlier, each packet is evaluated against the filter ruleset
from top to bottom.  By default, the packet is marked for passage, which
can be changed by any rule, and could be changed back and forth several
times before the end of the filter rules. <b>The last matching rule
"wins".</b>  There is an exception to this: The <tt>quick</tt> option
on a filtering rule has the effect of canceling any further rule
processing and causes the specified action to be taken. Let's look at a
couple examples:

<p>
Wrong:
<blockquote>
<tt>
block in on fxp0 proto tcp from any to any port ssh<br>
pass &nbsp;in all
</tt>
</blockquote>

<p>
In this case, the <tt>block</tt> line may be evaluated, but will never
have any effect, as it is then followed by a line which will pass
everything.

<p>
Better:
<blockquote>
<tt>
block in quick on fxp0 proto tcp from any to any port ssh<br>
pass &nbsp;in all
</tt>
</blockquote>

<p>
These rules are evaluated a little differently.  If the <tt>block</tt>
line is matched, due to the <tt>quick</tt> option, the packet will be
blocked, and the rest of the ruleset will be ignored.

<a name="state"></a>
<h2>Keeping State</h2>
One of Packet Filter's important abilities is "keeping state" or
"stateful inspection". Stateful inspection refers to PF's ability to
track the state, or progress, of a network connection. By storing
information about each connection in a state table, PF is able to
quickly determine if a packet passing through the firewall belongs to an
already established connection. If it does, it is passed through
the firewall without going through ruleset evaluation.

<p>
Keeping state has many advantages including simpler rulesets and better
packet filtering performance. PF is able to match packets moving in
<i>either</i> direction to state table entries meaning that filter rules
which pass returning traffic don't need to be written. And, since
packets matching stateful connections don't go through ruleset
evaluation, the time PF spends processing those packets can be greatly
lessened.

<p>
When a rule creates state, the first packet
matching the rule creates a "state" between the sender and receiver.  
Now, not only do packets going from the sender to receiver match the
state entry and bypass ruleset evaluation, but so do the reply packets
from receiver to sender.

<p>
Starting in OpenBSD 4.1, all filter rules automatically create a state
entry when a packet matches the rule.
In earlier versions of OpenBSD the filter rule had to explicitly use the
<tt>keep state</tt> option.

<p>
Example using OpenBSD 4.1 and later:
<blockquote>
<tt>
pass out on fxp0 proto tcp from any to any
</tt>
</blockquote>

<p>
Example using OpenBSD 4.0 and earlier:
<blockquote>
<tt>
pass out on fxp0 proto tcp from any to any keep state
</tt>
</blockquote>

<p>
These rules allow any outbound TCP traffic on the <tt>fxp0</tt> interface 
and also permits the reply traffic to pass back through the firewall.
While keeping state is a nice feature, its use significantly improves
the performance of your firewall as state lookups are dramatically
faster than running a packet through the filter rules.

<p>
The <tt>modulate state</tt> option works just like <tt>keep state</tt>
except that it only applies to TCP packets. With 
<tt>modulate state</tt>, the Initial Sequence Number (ISN) of outgoing
connections is randomized. This is useful for protecting connections
initiated by certain operating systems that do a poor job of choosing
ISNs.
Starting with OpenBSD 3.5, the <tt>modulate state</tt> option can be
used in rules that specify protocols other than TCP. 

<p>
Keep state on outgoing TCP, UDP, and ICMP packets and modulate TCP ISNs:
<blockquote>
<tt>
pass out on fxp0 proto { tcp, udp, icmp } from any \<br>
&nbsp;&nbsp;&nbsp;&nbsp;to any modulate state<br>
</tt>
</blockquote>

<p>
Another advantage of keeping state is that corresponding ICMP traffic
will be passed through the firewall.
For example, if a TCP connection passing through the firewall is being
tracked statefully and an ICMP source-quench message
referring to this TCP connection arrives, it will be matched to the
appropriate state entry and passed through the firewall.

<p>
The scope of a state entry is controlled globally by the 
<a href="options.html#state-policy"><tt>state-policy</tt></a>
runtime option and on a per rule basis by the <tt>if-bound</tt>,
<tt>group-bound</tt>, and <tt>floating</tt> state option keywords.
These per rule keywords have the same meaning as when used with the
<tt>state-policy</tt> option. Example:

<blockquote>
<tt>
pass out on fxp0 proto { tcp, udp, icmp } from any \<br>
&nbsp;&nbsp;&nbsp;&nbsp;to any modulate state (if-bound)<br>
</tt>
</blockquote>

<p>
This rule would dictate that in order for packets to match the state
entry, they must be transiting the <tt>fxp0</tt> interface.


<p>
Note that <a href="nat.html"><tt>nat</tt></a>, 
<a href="nat.html#binat"><tt>binat</tt></a>, and
<a href="rdr.html"><tt>rdr</tt></a> rules implicitly create state for
matching connections as long as the connection is passed by the
filter ruleset.

<a name="udpstate"></a>
<h2>Keeping State for UDP</h2>
One will sometimes hear it said that, "One can not create state with
UDP as UDP is a stateless protocol!"  While it is true that a UDP
communication session does not have any concept of state (an explicit
start and stop of communications), this does not have any impact on PF's
ability to create state for a UDP session.  In the case of protocols
without "start" and "end" packets, PF simply keeps track of how long it
has been since a matching packet has gone through.  If the timeout is
reached, the state is cleared.  The timeout values can be set in the 
<a href="options.html">options</a> section of the <tt>pf.conf</tt>
file.

<a name="stateopts"></a>
<h2>Stateful Tracking Options</h2>
Filter rules that create state entries can specify various options to
control the behavior of the resulting state entry.
The following options are available:

<dl>
<dt><tt>max <i>number</i></tt>
<dd>Limit the maximum number of state entries the rule can create to
<i>number</i>.
If the maximum is reached, packets that would normally create state
fail to match this rule until the number of existing states decreases
below the limit.

<dt><tt>no state</tt>
<dd>Prevents the rule from automatically creating a state entry.

<dt><tt>source-track</tt>
<dd>This option enables the tracking of number of states created per
source IP address.
This option has two formats:
	<ul>
	<li><tt>source-track rule</tt> - The maximum number of states
	created by this rule is limited by the rule's <tt>max-src-nodes</tt>
	and <tt>max-src-states</tt> options. Only state entries created by
	this particular rule count toward the rule's limits.
	<li><tt>source-track global</tt> - The number of states created by
	all rules that use this option is limited. Each rule can specify
	different <tt>max-src-nodes</tt> and <tt>max-src-states</tt>
	options, however state entries created by any participating rule
	count towards each individual rule's limits.
	</ul>
The total number of source IP addresses tracked globally can be
controlled via the 
<a href="options.html#limit"><tt>src-nodes</tt> runtime option</a>.

<dt><tt>max-src-nodes <i>number</i></tt>
<dd>When the <tt>source-track</tt> option is used,
<tt>max-src-nodes</tt> will limit the number of source IP addresses that
can simultaneously create state.
This option can only be used with <tt>source-track rule</tt>.

<dt><tt>max-src-states <i>number</i></tt>
<dd>When the <tt>source-track</tt> option is used,
<tt>max-src-states</tt> will limit the number of simultaneous state
entries that can be created per source IP address.
The scope of this limit (i.e., states created by this rule only or
states created by all rules that use <tt>source-track</tt>) is dependent
on the <tt>source-track</tt> option specified.
</dl>

<p>
Options are specified inside parenthesis and immediately after one of
the state keywords (<tt>keep state</tt>,
<tt>modulate state</tt>, or <tt>synproxy state</tt>).
Multiple options are separated by commas.
In OpenBSD 4.1 and later, the <tt>keep state</tt> option became the
implicit default for all filter rules.
Despite this, when specifying stateful options, one of the state keywords
must still be used in front of the options.


<p>
An example rule:

<blockquote>
<tt>
pass in on $ext_if proto tcp to $web_server \<br>
&nbsp;&nbsp;&nbsp;&nbsp;port www keep state \<br>
&nbsp;&nbsp;&nbsp;&nbsp;(max 200, source-track rule, max-src-nodes 100,
max-src-states 3)
</tt>
</blockquote>

<p>
The rule above defines the following behavior:

<ul>
<li>Limit the absolute maximum number of states that this rule can
create to 200 
<li>Enable source tracking; limit state creation based on states created
by this rule only
<li>Limit the maximum number of nodes that can simultaneously create
state to 100
<li>Limit the maximum number of simultaneous states per source IP to 3
</ul>

<p>
A separate set of restrictions can be placed on stateful TCP connections
that have completed the 3-way handshake.

<dl>
<dt><tt>max-src-conn <i>number</i></tt>
<dd>Limit the maximum number of simultaneous TCP connections
which have completed the 3-way handshake that a single host can make.
<dt><tt>max-src-conn-rate <i>number</i> / <i>interval</i></tt>
<dd>Limit the rate of new connections to a certain amount per time
interval.
</dl>

<p>
Both of these options automatically invoke the <tt>source-track rule</tt>
option and are incompatible with <tt>source-track global</tt>.

<p>
Since these limits are only being placed on TCP connections that have
completed the 3-way handshake, more aggressive actions can be taken on
offending IP addresses.

<dl>
<dt><tt>overload &lt;<i>table</i>&gt;</tt>
<dd>Put an offending host's IP address into the named table.
<dt><tt>flush [global]</tt>
<dd>Kill any other states that match this rule and that were created by
this source IP.
When <tt>global</tt> is specified, kill all states matching this source
IP, regardless of which rule created the state.
</dl>

<p>
An example:

<blockquote>
<tt>
table &lt;abusive_hosts&gt; persist<br>
block in quick from &lt;abusive_hosts&gt;<br>
<br>
pass in on $ext_if proto tcp to $web_server \<br>
&nbsp;&nbsp;&nbsp;&nbsp;port www flags S/SA keep state \<br>
&nbsp;&nbsp;&nbsp;&nbsp;(max-src-conn 100, max-src-conn-rate 15/5,
overload &lt;abusive_hosts&gt; flush)
</tt>
</blockquote>

<p>
This does the following:

<ul>
<li>Limits the maximum number of connections per source to 100
<li>Rate limits the number of connections to 15 in a 5 second span
<li>Puts the IP address of any host that breaks these limits into the
<tt>&lt;abusive_hosts&gt;</tt> table
<li>For any offending IP addresses, flush any states created by this
rule.
</ul>

<a name="tcpflags"></a>
<h2>TCP Flags</h2>
Matching TCP packets based on flags is most often used to filter TCP
packets that are attempting to open a new connection. The TCP flags and
their meanings are listed here:
<ul>
<li><b>F</b> : FIN  - Finish; end of session
<li><b>S</b> : SYN  - Synchronize; indicates request to start session
<li><b>R</b> : RST  - Reset; drop a connection
<li><b>P</b> : PUSH - Push; packet is sent immediately
<li><b>A</b> : ACK  - Acknowledgement
<li><b>U</b> : URG  - Urgent
<li><b>E</b> : ECE  - Explicit Congestion Notification Echo
<li><b>W</b> : CWR  - Congestion Window Reduced
</ul>

<p>
To have PF inspect the TCP flags during evaluation of a rule, the
<tt>flags</tt> keyword is used with the following syntax:
<blockquote>
<tt>
flags <i>check</i>/<i>mask</i><br>
flags any
</tt>
</blockquote>

<p>
The <tt><i>mask</i></tt> part tells PF to only inspect the specified flags
and the <tt><i>check</i></tt> part specifies which flag(s) must be
"on" in the header for a match to occur.
Using the <tt>any</tt> keyword allows any combination of flags to be set
in the header.
<blockquote>
<tt>
pass in on fxp0 proto tcp from any to any port ssh flags S/SA
</tt>
</blockquote>

<p>
The above rule passes TCP traffic with the SYN flag set while only
looking at the SYN and ACK flags. A packet with the SYN and ECE flags
would match the above rule while a packet with SYN and ACK or just ACK
would not.

<p>
In OpenBSD 4.1 and later, the default flags applied to TCP rules is
<tt>flags S/SA</tt>.
Combined with the OpenBSD 4.1 default of <tt>keep state</tt> on filter
rules, these two rules become equivalent:
<blockquote>
<tt>
pass out on fxp0 proto tcp all flags S/SA keep state<br>
pass out on fxp0 proto tcp all
</tt>
</blockquote>

<p>
Each rule will match TCP packets with the SYN flag set and ACK flag
clear and each will create a state entry for matching packets.
The default flags can be overridden by using the <tt>flags</tt> option
as outlined above.

<p>
In OpenBSD 4.0 and earlier there were no default flags applied to any
filter rules.
Each rule had to specify which flag(s) to match on and also had to
explicity use the <tt>keep state</tt> option.
<blockquote>
<tt>
pass out on fxp0 proto tcp all flags S/SA keep state
</tt>
</blockquote>

<p>
One should be careful with using flags -- understand what you are
doing and why, and be careful with the advice people give as a lot of 
it is bad.  Some people have suggested creating state "only if the SYN flag
is set and no others". Such a rule would end with: 
<pre>
     . . . flags S/FSRPAUEW  <i>bad idea!!</i>
</pre>

<p>
The theory is, create state only on the start of the TCP session, and
the session should start with a SYN flag, and no others.  The problem
is some sites are starting to use the ECN flag and any site using ECN
that tries to connect to you would be rejected by such a rule.
A much better guideline is to not specify any flags at all and let PF
apply the default flags to your rules.
If you truly need to specify flags yourself then this combination should
be safe:
<blockquote>
<tt>
. . . flags S/SAFR
</tt>
</blockquote>

<p>
While this is practical and safe, it is also unnecessary to check the
FIN and RST flags if traffic is also being
<a href="scrub.html">scrubbed</a>. The scrubbing process will cause PF
to drop any incoming packets with illegal TCP flag combinations (such as
SYN and RST) and to normalize potentially ambiguous combinations (such
as SYN and FIN).


<a name="synproxy"></a>
<h2>TCP SYN Proxy</h2>
<p>
Normally when a client initiates a TCP connection to a server, PF will
pass the 
<a href="http://www.inetdaemon.com/tutorials/internet/tcp/3-way_handshake.shtml"
>handshake</a> packets between the two endpoints as they arrive. 
PF has the ability, however, to proxy the handshake. 
With the handshake proxied, PF itself will complete the handshake with
the client, initiate a handshake with the server, and then pass packets
between the two. 
The benefit of this process is that no packets are sent to the server
before the client completes the handshake. 
This eliminates the threat of spoofed TCP SYN floods affecting the
server because a spoofed client connection will be unable to complete
the handshake.

<p>
The TCP SYN proxy is enabled using the <tt>synproxy state</tt> keywords
in filter rules. Example:

<blockquote>
<tt>
pass in on $ext_if proto tcp from any to $web_server port www \<br>
&nbsp;&nbsp;&nbsp;flags S/SA synproxy state
</tt>
</blockquote>

<p>
Here, connections to the web server will be TCP proxied by PF.

<p>
Because of the way <tt>synproxy state</tt> works, it also includes the
same functionality as <tt>keep state</tt> and <tt>modulate state</tt>.

<p>
The SYN proxy will not work if PF is running on a 
<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=bridge&amp;sektion=4"
>bridge(4)</a>.

<a name="antispoof"></a>
<h2>Blocking Spoofed Packets</h2>
Address "spoofing" is when an malicious user fakes the source IP address
in packets they transmit in order to either hide their real address or
to impersonate another node on the network. Once the user has spoofed
their address they can launch a network attack without revealing the
true source of the attack or attempt to gain access to network services
that are restricted to certain IP addresses.

<p>
PF offers some protection against address spoofing through the
<tt>antispoof</tt> keyword:

<blockquote>
<tt>
antispoof [log] [quick] for <i>interface</i> [<i>af</i>]
</tt>
</blockquote>

<dl>
<dt><tt>log</tt>
<dd>Specifies that matching packets should be logged via
<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=pflogd&amp;sektion=8&amp;manpath=OpenBSD+4.3"
>pflogd(8)</a>.

<dt><tt>quick</tt>
<dd>If a packet matches this rule then it will be considered the
"winning" rule and ruleset evaluation will stop.

<dt><tt><i>interface</i></tt>
<dd>The network interface to activate spoofing protection on. This can
also be a <a href="macros.html#lists">list</a> of interfaces.

<dt><tt><i>af</i></tt>
<dd>The address family to activate spoofing protection for, either
<tt>inet</tt> for IPv4 or <tt>inet6</tt> for IPv6.
</dl>

<p>
Example:
<blockquote>
<tt>
antispoof for fxp0 inet
</tt>
</blockquote>

<p>
When a ruleset is loaded, any occurrences of the <tt>antispoof</tt>
keyword are expanded into two filter rules. Assuming that interface
<tt>fxp0</tt> has IP address 10.0.0.1 and a subnet mask of 255.255.255.0
(i.e., a /24), the above <tt>antispoof</tt> rule would expand to:

<blockquote>
<tt>
block in on ! fxp0 inet from 10.0.0.0/24 to any<br>
block in inet from 10.0.0.1 to any
</tt>
</blockquote>

<p>
These rules accomplish two things:
<ul>
<li>Blocks all traffic coming from the 10.0.0.0/24 network that does
<i>not</i> pass in through <tt>fxp0</tt>. Since the 10.0.0.0/24 network is on the
<tt>fxp0</tt> interface, packets with a source address in that network block
should never be seen coming in on any other interface.
<li>Blocks all incoming traffic from 10.0.0.1, the IP address on
<tt>fxp0</tt>.
The host machine should never send packets to itself through an external
interface, so any incoming packets with a source address belonging to
the machine can be considered malicious.
</ul>

<p>
<b>NOTE</b>: The filter rules that the <tt>antispoof</tt> rule
expands to will also block packets sent over the loopback interface to
local addresses.
It's best practice to skip filtering on loopback interfaces anyways, but
this becomes a necessity when using antispoof rules:
<blockquote>
<tt>
set skip on lo0<br>
<br>
antispoof for fxp0 inet
</tt>
</blockquote>

<p>
Usage of <tt>antispoof</tt> should be restricted to interfaces that have
been assigned an IP address. Using <tt>antispoof</tt> on an interface
without an IP address will result in filter rules such as:
<blockquote>
<tt>
block drop in on ! fxp0 inet all<br>
block drop in inet all
</tt>
</blockquote>

<p>
With these rules there is a risk of blocking <i>all</i> inbound traffic
on <i>all</i> interfaces.

<a name="urpf"></a>
<h2>Unicast Reverse Path Forwarding</h2>

<p>
Starting in <a href="../../40.html">OpenBSD 4.0</a>, PF offers a Unicast
Reverse Path Forwarding (uRPF) feature.
When a packet is run through the uRPF check, the source IP address of
the packet is looked up in the routing table.
If the outbound interface found in the routing table entry is the same as
the interface that the packet just came in on, then the uRPF check
passes.
If the interfaces don't match, then it's possible the packet has had its
source address spoofed.

<p>
The uRPF check can be performed on packets by using the
<tt>urpf-failed</tt> keyword in filter rules:

<blockquote>
<tt>
block in quick from urpf-failed label uRPF
</tt>
</blockquote>

<p>
Note that the uRPF check only makes sense in an environment where
routing is symmetric.

<p>
uRPF provides the same functionality as
<a href="#antispoof">antispoof</a> rules.


<a name="osfp"></a>
<h2>Passive Operating System Fingerprinting</h2>

<p>
Passive OS Fingerprinting (OSFP) is a method for passively detecting the
operating system of a remote host based on certain characteristics
within that host's TCP SYN packets. 
This information can then be used as criteria within filter rules.

<p>
PF determines the remote operating system by comparing characteristics
of a TCP SYN packet against the 
<a href="options.html#fingerprints">fingerprints file</a>, which by
default is 
<a
href="http://www.openbsd.org/cgi-bin/man.cgi?query=pf.os&amp;sektion=5&amp;manpath=OpenBSD+4.3"
><tt>/etc/pf.os</tt></a>. 
Once PF is enabled, the current fingerprint list can be viewed with this
command:

<blockquote>
<tt>
# pfctl -s osfp
</tt>
</blockquote>

<p>
Within a filter rule, a fingerprint may be specified by OS class,
version, or subtype/patch level. 
Each of these items is listed in the output of the <tt>pfctl</tt>
command shown above. To specify a fingerprint in a filter rule, the
<tt>os</tt> keyword is used:

<blockquote>
<tt>
pass &nbsp;in on $ext_if from any os OpenBSD keep state<br>
block in on $ext_if from any os "Windows 2000"<br>
block in on $ext_if from any os "Linux 2.4 ts"<br>
block in on $ext_if from any os unknown
</tt>
</blockquote>

<p>
The special operating system class <tt>unknown</tt> allows for matching
packets when the OS fingerprint is not known.

<p>
<font color="#ff0000">TAKE NOTE</font> of the following:
<ul>
  <li>Operating system fingerprints are occasionally wrong due to
  spoofed and/or crafted packets that are made to look like they
  originated from a specific operating system.
  <li>Certain revisions or patchlevels of an operating system may change
  the stack's behavior and cause it to either not match what's in the
  fingerprints file or to match another entry altogether.
  <li>OSFP only works on the TCP SYN packet; it will not work on other
  protocols or on already established connections.
</ul>

<a name="ipopts"></a>
<h2>IP Options</h2>
By default, PF blocks packets with IP options set. This can make the job
more difficult for "OS fingerprinting" utilities like nmap. If you have
an application that requires the passing of these packets, such as
multicast or IGMP, you can use the <tt>allow-opts</tt> directive:
<blockquote>
<tt>
pass in quick on fxp0 all allow-opts
</tt>
</blockquote>

<a name="example"></a>
<h2>Filtering Ruleset Example</h2>
Below is an example of a filtering ruleset. The machine running PF is
acting as a firewall between a small, internal network and the Internet.
Only the filter rules are shown; 
<a href="queueing.html">queueing</a>, 
<a href="nat.html"><tt>nat</tt></a>, 
<a href="rdr.html"><tt>rdr</tt></a>, 
etc., have been left out of this example.
<br>
<br>
<table border=0 width="650">
<tr><td nowrap bgcolor="#EEEEEE">
<pre>
ext_if  = "fxp0"
int_if  = "dc0"
lan_net = "192.168.0.0/24"

# table containing all IP addresses assigned to the firewall
table &lt;firewall&gt; const { self }

# don't filter on the loopback interface
set skip on lo0

# scrub incoming packets
scrub in all

# setup a default deny policy
block all

# activate spoofing protection for all interfaces
block in quick from urpf-failed

# only allow ssh connections from the local network if it's from the
# trusted computer, 192.168.0.15. use "block return" so that a TCP RST is
# sent to close blocked connections right away. use "quick" so that this
# rule is not overridden by the "pass" rules below.
block return in quick on $int_if proto tcp from ! 192.168.0.15 \
   to $int_if port ssh

# pass all traffic to and from the local network.
# these rules will create state entries due to the default
# "keep state" option which will automatically be applied.
pass in  on $int_if from $lan_net to any
pass out on $int_if from any to $lan_net

# pass tcp, udp, and icmp out on the external (Internet) interface. 
# tcp connections will be modulated, udp/icmp will be tracked
# statefully.
pass out on $ext_if proto { tcp udp icmp } all modulate state

# allow ssh connections in on the external interface as long as they're
# NOT destined for the firewall (i.e., they're destined for a machine on
# the local network). log the initial packet so that we can later tell
# who is trying to connect. use the tcp syn proxy to proxy the connection.
# the default flags "S/SA" will automatically be applied to the rule by
# PF.
pass in log on $ext_if proto tcp from any to ! &lt;firewall&gt; \
   port ssh synproxy state
</pre>
</td></tr>
</table>

<p>
[<a href="tables.html">Previous: Tables</a>]
[<a href="index.html">Contents</a>]
[<a href="nat.html">Next: Network Address Translation</a>]

<p>
<hr>
<a href="index.html"><img height="24" width="24" src="../../images/back.gif" border="0" alt="[back]"></a> 
<a href="mailto:www@openbsd.org">www@openbsd.org</a>
<br>
<small>$OpenBSD: filter.html,v 1.48 2008/07/27 17:13:47 nick Exp $</small>

</body>
</html> 
